package com.key.modules.shiro.global;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.key.common.constants.Constants;
import com.key.component.jwt.JwtUtil;
import com.key.modules.entity.SysMenu;
import com.key.modules.entity.SysRole;
import com.key.modules.entity.SysUser;
import com.key.modules.mapper.SysMenuMapper;
import com.key.modules.mapper.SysRoleMapper;
import com.key.modules.mapper.SysUserMapper;
import com.key.modules.shiro.MySimpleByteSource;
import com.key.modules.shiro.OAuth2Token;
import com.key.modules.shiro.ShiroUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @Author：朕
 * @Package：com.key.modules.shiro.global
 * @Project：logistics_system
 * @name：AuthRealm
 * @Date：2023/3/25 14:29
 * @Filename：AuthRealm
 */
@Slf4j
public class AuthRealm extends AuthorizingRealm {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private SysRoleMapper sysRoleMapper;

    @Autowired
    private SysMenuMapper sysMenuMapper;

    @Override
    public boolean supports(AuthenticationToken token) {
        if(token instanceof UsernamePasswordToken){
            return true;
        }
        if(token instanceof OAuth2Token){
            return true;
        }
        return false;
    }

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("=====进入授权方法======"+principalCollection.getPrimaryPrincipal());
        //根据账户名查询系统用户信息
//        String userName =  String.valueOf(principalCollection.getPrimaryPrincipal());
        // 根据用户名获取数据库中的用户信息
//        QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
//        queryWrapper.eq("username",userName);
//        SysUser sys_user = sysUserMapper.selectOne(queryWrapper);
        SysUser sys_user = BeanUtil.copyProperties(principalCollection.getPrimaryPrincipal(),SysUser.class);
//        SysUser sys_user = (SysUser)  principalCollection.getPrimaryPrincipal();
        List<SysRole> sysRoleList = sysRoleMapper.findSysRoleByUserId(sys_user.getUserId());
        if(sysRoleList.size() > 0){
            //存放角色Id，可能会有多个
            List<Integer> role_ids = new ArrayList<>();
            //遍历去重
            Set<String> roleSets = new HashSet<>();
            for (SysRole r:sysRoleList) {
                roleSets.add(r.getRoleName());
                //将每一次循环的角色ID存放到集合中
                role_ids.add(r.getRoleId());
            }
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            //授权-授角色
            info.setRoles(roleSets);
            //根据角色id查看对应的权限
            List<SysMenu> sysMenuList = sysMenuMapper.findMenuListByRoleIds(role_ids);
            if(sysMenuList.size() > 0){
                //遍历去重
                Set<String> menuSets = new HashSet<>();
                for (SysMenu sm:sysMenuList) {
                    menuSets.add(sm.getMenuPerms());
                }
                //授权，授对应的权限
                info.setStringPermissions(menuSets);
            }
            return info;
        }
        return null;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken){
        String user_account = null;
        if(authenticationToken instanceof OAuth2Token){
            String token = authenticationToken.getPrincipal().toString();
            user_account =  JwtUtil.parseJWT(token).getSubject();
        }else{
            user_account = authenticationToken.getPrincipal().toString();
        }
        System.out.println("userAccount:"+user_account);
        System.out.println("进入认证");
        log.info("到这里就结束了");
        log.info("{}系统用户进入认证方法",user_account);
        // 根据用户名获取数据库中的用户信息
        QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_account",user_account);
        SysUser sys_user = sysUserMapper.selectOne(queryWrapper);

        if(sys_user == null){
            throw new UnknownAccountException();
        }
        if(sys_user.getUserState() == Constants.ACCOUNT_LOCK){//冻结
            throw new LockedAccountException();
        }
        /* 传入密码自动判断是否正确
         * 参数1：传入对象给Principal
         * 参数2：正确的用户密码
         * 参数3：加盐处理
         * 参数4：固定写法
         */
        // 获取数据库中对应账户的盐值 == 对盐进行加密处理
//        ByteSource salt = ByteSource.Util.bytes(sys_user.getSalt());
        MySimpleByteSource salt = new MySimpleByteSource(sys_user.getUserSalt());
        log.info("查询系统用户进行密码验证匹配，密码：{}, 盐值：{}",sys_user.getUserPassword(),salt);
        //数据库中的密码 == 客户端传入的密码进行比较 --- 交给shiro去做
        return new SimpleAuthenticationInfo(sys_user, sys_user.getUserPassword(), salt, getName());
    }

    /**
     * 自定义密码验证匹配器
     */
    @PostConstruct
    public void initCredentialsMatcher() {
        setCredentialsMatcher(new SimpleCredentialsMatcher() {
            @Override
            public boolean doCredentialsMatch(AuthenticationToken authenticationToken, AuthenticationInfo authenticationInfo) {
                //如果传递token，那无需进行密码的校验
                if(authenticationToken instanceof OAuth2Token){
                    return true;
                }
                UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
                SimpleAuthenticationInfo info = (SimpleAuthenticationInfo) authenticationInfo;
                // 获取明文密码及密码盐
                String password = String.valueOf(token.getPassword());
                String salt = CodecSupport.toString(info.getCredentialsSalt().getBytes());
                System.out.println("密码");
                System.out.println(ShiroUtil.encrypt(password, salt));
//                System.out.println(info.getCredentials());
                return equals(ShiroUtil.encrypt(password, salt), info.getCredentials());
            }
        });
    }

}
